// Copyright (c) 2021 Presto Labs Pte. Ltd.
// Author: leon

#pragma once

#include <string>

#include <rapidjson/document.h>
#include <rapidjson/rapidjson.h>

#include "coin/proto/coin_market_enums.pb.h"
#include "coin2/base/conversion.h"
#include "coin2/exchange/base/feed/update.h"
#include "coin2/exchange/base/feed_util/book_builder.h"
#include "coin2/exchange/base/feed_util/trade.h"

namespace impl {
using coin2::exchange::base::market::ExchangeType;
using coin2::exchange::base::market::MarketType;

class GopaxFeedUpdateParser {
 public:
  static void ParseBook(const rapidjson::Value& book_json, BookBuilder* book_builder) {
    double ts = 0;
    auto tx = book_builder->NewTransaction(0);
    if (book_json.HasMember("maxEntryId")) {
      tx.Clear();
    }

    for (const auto& ask : book_json["ask"].GetArray()) {
      tx.UpdateAsk(stod(ask["price"].GetString()), stod(ask["volume"].GetString()));
      auto ts_ = stod(ask["updatedAt"].GetString());
      if (ts_ > ts) {
        ts = ts_;
      }
    }
    for (const auto& bid : book_json["bid"].GetArray()) {
      tx.UpdateBid(stod(bid["price"].GetString()), stod(bid["volume"].GetString()));
      auto ts_ = stod(bid["updatedAt"].GetString());
      if (ts_ > ts) {
        ts = ts_;
      }
    }
    tx.UpdateTimestamp(std::llround(ts * 1e9));
  }

  static void ParseTrade(const rapidjson::Value& transaction, Trade* trade) {
    int64_t timestamp = stoull(transaction["occurredAt"].GetString()) * 1'000'000'000LL;
    trade->price = stod(transaction["price"].GetString());
    trade->fill_qty = std::stod(transaction["baseAmount"].GetString());
    trade->side = ParseTradeSide(transaction);
    trade->timestamp = timestamp;
  }

  static coin2::exchange::feed::TradeSide ParseTradeSide(const rapidjson::Value& transaction) {
    bool is_buy = transaction["isBuy"].GetBool();
    if (is_buy) {
      return coin2::exchange::feed::TRADE_BUY_SIDE;
    } else {
      return coin2::exchange::feed::TRADE_SELL_SIDE;
    }
  }
};
}  // namespace impl

using impl::GopaxFeedUpdateParser;
